/**
 * Copyright 2007 Christian Mueller
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 
 * use this file except in compliance with the License. You may obtain a copy of 
 * the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by 
 * applicable law or agreed to in writing, software distributed under the License 
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 
 * either express or implied. See the License for the specific language governing 
 * permissions and limitations under the License.
 * 
 * Description:
 * Makes a full backup of databases
 * 
 */
 
 
 /**** Includes ****/
#include <stdio.h>
#include <windows.h>
#define _WINDEF_
#undef _WIN32_WINNT
#include "logging.h"
#include "nbackup.h"
#include "ncopy.h"
#include "globopts.h"
#include "w32io.h"

#include <global.h>
#include <nsfdb.h>
#include <nsfsearc.h>
#include <osfile.h>
#include <ods.h>
#include <odstypes.h>
#include <textlist.h>

/**
 * run_make_backup()
 * 
 * Makes a full backup of the data directory including all
 * subdirectorys.
 * 
 * It loads the global parameter opt_dest_direntry and initiate
 * the function backup with this parameter.
 * 
 */
void run_make_backup(){

 	LOG_DEBUG1("void run_make_backup() - start");
 	
 	/** Check Database **/
 	LOG_DEBUG2("check opt_dest_direntry");
	if (opt_dest_dir[0]=='\0') {
		LOG_ERROR("Please define the destination directory with option --dir <string>");
		return;	
	}
	
	/* Start backup to defined directory */
	backup(opt_dest_dir);
	 	
 	LOG_DEBUG1("void run_make_backup() - end");
}

/**
 * backup(const char* destdir)
 * 
 * This function makes a online backup of the complete data directory
 * 
 * Online means that the server oder client could run while the backup
 * is made, without corrupt databases using the ncopy function.
 * 
 */
int backup(const char* destdir) {
	LOG_DEBUG1("void backup(char* destdir) - start");
	
	STATUS      error = NOERROR;
	DBHANDLE    dir_handle;
	
	/*
	 * Open the directory handler. "" means the root of the
	 * data directory
	 */	     
	LOG_DEBUG3("call NSFDbOpen (\"\", &dir_handle)");
	error = NSFDbOpen ("", &dir_handle);
	if (error)
    {
       LOG_ERROR("Database directory couldn't be opened!");
       return (8);
    }
	
	LOG_INFO("Online backup of databases....");
	
	/* Check what is to be processed */
	LOG_DEBUG3("Search recursive throu all subdirectorys.");
	WORD searchflags = FILE_RECURSE+FILE_ANYNOTEFILE; /* Go Recursive throu all subdirectorys */

	if (opt_box_files_only==TRUE) {
		LOG_INFO("Search only for *.BOX files...");
		searchflags=FILE_RECURSE+FILE_MAILBOX;
	} else if (opt_nsf_files_only==TRUE) {
		LOG_INFO("Search only for *.NSF, *.NSG and  *.NSH files...");
		searchflags=FILE_RECURSE+FILE_DBANY;
	} else if (opt_ntf_files_only==TRUE) {
		LOG_INFO("Search only for *.NTF files...");
		searchflags=FILE_RECURSE+FILE_FTANY;
	}
	
	/* Retrieve now all databases */
	LOG_DEBUG3("call NSFSearch(...)");
	LOG_INFO("Starting Backup...");
	error = NSFSearch (
        dir_handle,        /* directory handle */
        NULLHANDLE,        /* selection formula */
        NULL,              /* title of view in formula */
        SEARCH_FILETYPE +  /* search for files */
        SEARCH_SUMMARY,    /* return a summary buffer */
        searchflags,	   /* define the searchflags */
        NULL,              /* starting date */
        backup_database,   /* call backup_database for each file found */
        NULL,              /* no additional arguments to action routine */
        NULL);
        
	if (error)             /* returned ending date (unused) */

        {
        	print_api_error (error);
            LOG_ERROR("Databases couldn't be retrieved or found!");
            NSFDbClose (dir_handle);
            return (8);
        }
	
	
	/** Close directory **/
	LOG_DEBUG3("call NSFDbClose (dir_handle)");
	error = NSFDbClose (dir_handle);
	if (error)
    {
    	LOG_ERROR("Database directory couldn't be closed!");
    	return 8;
    }
    LOG_INFO("Backup finished");
	LOG_DEBUG1("void backup(char* destdir) - end");
	return 0;
}

/*
 * getfield(ITEM_TABLE *summary, char* path)
 * 
 * This function simply retrieves the $PATH value
 * of the given summary. So that the filepath of the
 * database is known
 */
void getfieldtext(ITEM_TABLE *summary, char* path) {
	LOG_DEBUG2("void getfieldtext() - start");
	#define     MAX_ITEMS           50
	#define     MAX_TEXT_LEN        1000
	#define     DATATYPE_SIZE       sizeof(USHORT)
	#define     NAME_LENGTH_SIZE    sizeof(USHORT)
	#define     ITEM_LENGTH_SIZE    sizeof(USHORT)
	ITEM_TABLE item_table;
	USHORT     item_count;              /* number of items in summary */
    USHORT     name_length[MAX_ITEMS];  /* length of each item name */
    USHORT     item_length[MAX_ITEMS];  /* length of each item */
    char       item_name[MAX_TEXT_LEN]; /* name of a summary item */
    BYTE       *summary_position;
    USHORT     i;
    
    /* Retrieving the basic informations */
    item_table = *summary;
    summary_position = (BYTE *) summary;
    item_count = item_table.Items;
    
    /* Check if to many items in the summary */
    if (item_count > MAX_ITEMS)	{
    	LOG_ERROR("Too much items...abort!");
    	return;
    }
    
    summary_position += ODSLength(_ITEM_TABLE);

	/* get the length of the itemnames and itemvalues*/
	for (i=0; i < item_count; i++) {
		memcpy (&name_length[i], summary_position, NAME_LENGTH_SIZE);
		summary_position += NAME_LENGTH_SIZE;
		memcpy (&item_length[i], summary_position, ITEM_LENGTH_SIZE);
		summary_position += ITEM_LENGTH_SIZE;
    }
        
    /* Process all items */
    for (i=0; i < item_count; i++) {
    	/** Get the name **/
    	memcpy (item_name, summary_position, name_length[i]);
        item_name[name_length[i]] = '\0';
        summary_position += name_length[i];
        
        /* Retriev the datatype of this item */
        USHORT     datatype;
        memcpy (&datatype, summary_position, DATATYPE_SIZE);
        summary_position += DATATYPE_SIZE;
              
        /* Process only Textvalues with the itemname $path */
        if (datatype == TYPE_TEXT && stricmp(item_name,"$Path") == 0) {
        	memcpy (path,
                summary_position,
                item_length[i] - DATATYPE_SIZE);
            path[item_length[i] - DATATYPE_SIZE] = '\0';
            /* set the retrieved path and break the for loop */
        	break;
        } // end-if
    	
    	/* Go to the next entry */	
    	summary_position += (item_length[i] - DATATYPE_SIZE);
    } // end-for
    
    LOG_DEBUG2("void getfieldtext() - end");
}

/*
 * backup_database(void *current_path,SEARCH_MATCH far *pSearchMatch,
 *          ITEM_TABLE *summary_info)
 *           
 * This function is called for each found database via NSFSearch in
 * the backup(...) function.
 * This function retrieves the path from the given summary informations.
 * Create the subdirectorys and initiate the copy.
 */
STATUS backup_database(void *current_path,SEARCH_MATCH far *pSearchMatch,
            ITEM_TABLE *summary_info) {
     LOG_DEBUG1("void backup_database(...) - start");
     
     char database[MAXPATH]="";
     char destfile[MAXPATH]="";
     char destpath[MAXPATH]="";
     
     /* Get the path entry */
     getfieldtext(summary_info,database);
     
     if (database[0]=='\0') {
     	// end processing if the $Path entry is not found
     	LOG_DEBUG1("The summary didn't contain a $Path entry...");
     	return;     	
     }
     
     /* Generate the destination filename */
     strcpy(destfile,opt_dest_dir);
     strcat(destfile,"\\");
     strcat(destfile,database);
     
     /** Get path and create it, if not exist **/
     strcpy(destpath,destfile);
     
     /* for path retriev go backwards throu the destpath
      * stop if slash or backslash is found */
     int i;
     for(i=strlen(destpath);i>0;i--) {
     	if (destpath[i]=='\\' || destpath[i]=='/') {
     		/* cut here! */
     		destpath[i]='\0';
     		break;
     	}
     }
     
     
     if (HAS_DEBUG2) {
		char debug_string[256];
		sprintf(debug_string,"databasename = %s\n",database);
		LOG_DEBUG2(debug_string);
		sprintf(debug_string,"destfile = %s\n",destfile);
		LOG_DEBUG2(debug_string);
		sprintf(debug_string,"destpath = %s\n",destpath);
		LOG_DEBUG2(debug_string);
	}
	
	/* create directory, if not exist. Needed for subdirectorys */
	LOG_DEBUG3("call SysFileCreateDirectory(destpath)");
	SysFileCreateDirectory(destpath);
	
	/* initiate the backupcopy for the defined file */
	int ret = simpleCopy(database,destfile);
	
	char message[512];
	if (ret==0) {
		sprintf(message,"Database %s successfully copied.",database);
		LOG_INFO(message);
	} else {
		sprintf(message,"Database %s wasn't copied!",database);
		LOG_ERROR(message);	
	}
	
    LOG_DEBUG1("void backup_database(...) - end");
    return (NOERROR);        	
}

